package org.demo.compiler;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

/**
 * 动态编译
 * 参考: http://www.infoq.com/cn/articles/cf-java-byte-code
 * @author  
 * @date    2010-12-26
 * @file    org.demo.compiler.DynamicCompiler.java
 */
public class DynamicCompiler {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		System.out.println(calculate("(3+4)*7 - 10"));
	}
	/**
	 * 计算表达式的值
	 * @param expression
	 * @return
	 */
	private static double calculate(String expression){
		// class info
		String className = "CalculatorMain";
	    String methodName = "calculate";
	    String source = "public class " + className + "{"
	    	+ " public static double " + methodName + "(){"
	    	+ "  return " + expression + ";"
	    	+ " }"
	    	+ "}";
	    // compilation units
	    StringJavaFileObject obj = new StringJavaFileObject(className, source);
	    Iterable<? extends JavaFileObject> fileObjs = Arrays.asList(obj);
	    // compiler options
	    String path = DynamicCompiler.class.getClassLoader()
		                             .getResource("").getPath();
		if (path.startsWith("/")){
			path = path.substring(1);
		}
		List<String> options = new ArrayList<String>();
		options.add("-d");
		options.add(path);
		// compile the dynamic class
	    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
		CompilationTask task = compiler.getTask(null, fileMgr, null, options, null, fileObjs);
		boolean result = task.call();
		// invoke the method
		if (result){			
			ClassLoader loader = DynamicCompiler.class.getClassLoader();
			try {
				Class<?> clazz = loader.loadClass(className);
				Method method = clazz.getMethod(methodName, new Class[]{});
				Object value = method.invoke(null, new Object[]{});
				return (Double)value;
			} catch (Exception e){
				e.printStackTrace();
				throw new RuntimeException("内部错误: " + e.getMessage());
			}
		} else {
			throw new RuntimeException("错误的表达式: " + expression);
		}
	}	
	/**
	 * 基于 String 的 java 对象
	 * @author 
	 *
	 */
	static class StringJavaFileObject extends SimpleJavaFileObject{

		private String content = null;
		
		protected StringJavaFileObject(String className, String content) {
			super(
			    URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), 
				Kind.SOURCE
		    );
			this.content = content;
		}
		
		@Override
		public CharSequence getCharContent(boolean ignoreEncodingErrors)
				throws IOException {
			return content;
		}
	}
}
